package org.unidata.mdm.meta.service.impl.sourcesystems.instance;

import java.util.Collection;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

import org.unidata.mdm.core.service.CustomPropertiesSupport;
import org.unidata.mdm.core.type.model.SourceSystemElement;
import org.unidata.mdm.core.type.model.instance.AbstractModelInstanceImpl;
import org.unidata.mdm.core.type.model.instance.AbstractNamedDisplayableCustomPropertiesImpl;
import org.unidata.mdm.meta.configuration.TypeIds;
import org.unidata.mdm.meta.type.instance.SourceSystemsInstance;
import org.unidata.mdm.meta.type.model.sourcesystem.SourceSystem;
import org.unidata.mdm.meta.type.model.sourcesystem.SourceSystemsModel;
import org.unidata.mdm.meta.util.ModelUtils;

/**
 * @author Mikhail Mikhailov on Oct 7, 2020
 * Source systems holder.
 */
public class SourceSystemsInstanceImpl extends AbstractModelInstanceImpl<SourceSystemsModel>
    implements SourceSystemsInstance, CustomPropertiesSupport {
    /**
     * All systems.
     */
    private final Map<String, SourceSystemElement> systems;
    /**
     * Ascending weights map.
     */
    private final Map<String, Integer> ascending;
    /**
     * Descending weights map.
     */
    private final Map<String, Integer> descending;
    /**
     * The admin element.
     */
    private SourceSystemElement admin;
    /**
     * Constructor.
     */
    public SourceSystemsInstanceImpl(SourceSystemsModel ss) {
        super(ss);
        this.ascending = ModelUtils.createSourceSystemsMap(ss.getValues(), false);
        this.descending = ModelUtils.createSourceSystemsMap(ss.getValues(), true);
        this.systems = ss.getValues().stream()
                .map(SourceSystemImpl::new)
                .map(v -> {
                    if (v.isAdmin()) {
                        admin = v;
                    }
                    return v;
                })
                .collect(Collectors.toMap(SourceSystemImpl::getName, Function.identity()));
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public SourceSystemsModel toSource() {
        return new SourceSystemsModel()
                .withCreateDate(getCreateDate())
                .withCreatedBy(getCreatedBy())
                .withVersion(getVersion())
                .withValues(getSourceSystems().stream()
                        .map(ssi -> new SourceSystem()
                            .withAdmin(ssi.isAdmin())
                            .withName(ssi.getName())
                            .withDisplayName(ssi.getDisplayName())
                            .withWeight(ssi.getWeight())
                            .withCustomProperties(assembleCustomProperties(ssi.getCustomProperties())))
                        .collect(Collectors.toList()));
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public String getInstanceId() {
        return ModelUtils.DEFAULT_MODEL_INSTANCE_ID;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public String getTypeId() {
        return TypeIds.SOURCE_SYSTEMS_MODEL;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean isEmpty() {
        return systems.isEmpty();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public SourceSystemElement getAdminElement() {
        return admin;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public SourceSystemElement getSourceSystem(String name) {
        return systems.get(name);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public boolean exists(String name) {
        return systems.containsKey(name);
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public Collection<SourceSystemElement> getSourceSystems() {
        return systems.values();
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public Map<String, Integer> getAscendingMap() {
        return ascending;
    }
    /**
     * {@inheritDoc}
     */
    @Override
    public Map<String, Integer> getDescendingMap() {
        return descending;
    }
    /**
     * @author Mikhail Mikhailov on Oct 7, 2020
     * Source system implementation.
     */
    public static class SourceSystemImpl extends AbstractNamedDisplayableCustomPropertiesImpl implements SourceSystemElement {
        /**
         * This system weight.
         */
        private final int weight;
        /**
         * Admin mark.
         */
        private final boolean admin;
        /**
         * The source.
         */
        private final SourceSystem source;
        /**
         * Constructor.
         * @param ss the source system
         */
        private SourceSystemImpl(SourceSystem ss) {
            super(ss.getName(), ss.getDisplayName(), ss.getCustomProperties());
            this.weight = ss.getWeight();
            this.admin = ss.isAdmin();
            this.source = ss;
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public String getId() {
            return getName();
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public int getWeight() {
            return weight;
        }
        /**
         * {@inheritDoc}
         */
        @Override
        public boolean isAdmin() {
            return admin;
        }
        /**
         * @return the source
         */
        public SourceSystem getSource() {
            return source;
        }
    }
}
